package cn.csc.module.pay.job.reconciliation;

import cn.csc.framework.common.util.date.DateUtils;
import cn.csc.framework.pay.core.client.PayClient;
import cn.csc.framework.pay.core.client.impl.tl.TLLitePayClient;
import cn.csc.framework.pay.core.client.impl.tl.TLPayClientConfig;
import cn.csc.framework.pay.core.client.impl.tl.util.SybUtil;
import cn.csc.module.pay.dal.dataobject.merchant.PayChannelDO;
import cn.csc.module.pay.enums.ErrorCodeConstants;
import cn.csc.module.pay.service.merchant.PayChannelService;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.http.HttpUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static cn.csc.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 第三方支付对账Job
 * 通联支付对账
 * @author liuchuang
 * @version 1.0
 * @date 2023/11/23 10:19
 * @modify
 */
@Component
@Slf4j
public class ThirdPayReconciliation {

    @Resource
    private PayChannelService channelService;

    //需要下载对账文件的应用的APPID
    @Value("${csc.reconciliation.appId}")
    private BigInteger appId;
    @Value("${csc.reconciliation.tlCode}")
    private String tlCode;

    @Value("${csc.ftp.host}")
    private String host;
    @Value("${csc.ftp.port}")
    private Integer port;
    @Value("${csc.ftp.username}")
    private String username;
    @Value("${csc.ftp.passwd}")
    private String passwd;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @XxlJob("tlReconciliationJob")
    public ReturnT<String> tlReconciliationJob() throws Exception {
        String jobParams = XxlJobHelper.getJobParam();
        log.info("[tlReconciliationJob]输入的任务参数: {}", jobParams);
        // 日期输入参数若不为空，则必须为当前日期之前，格式为yyyyyMMdd
        if (StringUtils.hasLength(jobParams) && DateUtils.afterEqualNow(DateUtils.str2Date(jobParams))){
            log.info("[tlReconciliationJob]日期输入参数若不为空，则必须为当前日期之前，格式为yyyyyMMdd！");
            return new ReturnT<>(500, "日期输入参数若不为空，则必须为当前日期之前，格式为yyyyyMMdd！");
        }

        // 校验支付渠道是否有效
        PayChannelDO channel = channelService.validPayChannel(appId, null, tlCode);
        log.info("[tlReconciliationJob]channel：{}", channel);

        // 校验支付客户端是否正确初始化
        PayClient client = channelService.getPayClient(channel.getId());
        log.info("[tlReconciliationJob]client：{}", client);
        if (client == null) {
            log.error("[tlReconciliationJob][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
            throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
        }

        // 随机码
        String code = SybUtil.getValidatecode(8);
        // 昨天 yyyyMMdd
        String dateStr = Optional.ofNullable(jobParams).orElse(DateUtils.date2Str(DateUtils.dateDayPlus(-1L), DateUtils.FORMAT_YEAR_MONTH_DAY_S));
        log.info("[tlReconciliationJob]dateStr：{}", dateStr);
        Map<String, String> map = ((TLLitePayClient)client).getPayService()
                .trxFileGet("cusapi/trxfile/get", dateStr,  code,  "1");
        log.info("[tlReconciliationJob]通联对账接口执行结果：{}", map);

        String filePath = "/opt/csc/pay-server/file/tmp";
        if (!FileUtil.exist(filePath)){
            FileUtil.mkdir(filePath);
        }
        String mchId = ((TLPayClientConfig) channel.getConfig()).getMchId();
        File file = new File(filePath+File.separator+mchId+".zip");
        HttpUtil.downloadFile(map.get("url"), file);

        ZipUtil.unzip(file, StandardCharsets.UTF_8);

        log.info("[tlReconciliationJob]ls：{}", FileUtil.ls(filePath));

        // 上传到ftp
        Ftp ftp = new Ftp(host, port, username, passwd);
        if (!ftp.exist(mchId+dateStr)){
            ftp.mkdir(mchId+dateStr);
        }
        boolean upload = ftp.upload(mchId + dateStr, new File(mchId + File.separator + mchId + ".xlsx"));
        log.info("[tlReconciliationJob]:{}上传通联商户{}对账文件{}", dateStr, mchId, upload ? "成功":"失败");

        return upload ? new ReturnT<>("ftp上传文件成功"): new ReturnT<>(500, "ftp上传文件失败！");
    }

    public void lockTest(){
        String lockKey = appId+"_"+tlCode;
        // 24小时
        String uuid = UUID.randomUUID().toString();
        stringRedisTemplate.opsForValue().set(lockKey, uuid,24, TimeUnit.HOURS);

        // lockKey不存在时加锁 300 s
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 300, TimeUnit.SECONDS); //保证锁的赋值和过期时间设置的原子性，同时过期时间避免出现死锁问题
        if(lock){
            //加锁成功,执行业务
            try{
                // 执行业务
            }finally {
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                //删除锁
                stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(lockKey), uuid);
            }
        }

    }


}
